
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import javax.imageio.ImageIO;

public class Map {
	
	/*
	 * Map name and map title are different properties; the previous one
	 * is applied for background operations, while the latter one is the
	 * string shown on the screen (say, foreground).
	 */
	public static final String MAP_NAME_0 = "map_0";
	public static final String MAP_NAME_1 = "map_1";
	public static final String MAP_NAME_2 = "map_2";
	public static final String MAP_NAME_3 = "map_3";
	private static final String MAP_TITLE_0 = "";
	private static final String MAP_TITLE_1 = "";
	private static final String MAP_TITLE_2 = "";
	private static final String MAP_TITLE_3 = "";
	
	private String mapName;
	private String mapTitle;
	private int height;	// The metric of height is blocks
	private int width;	// The metric of width is blocks
	private ArrayList<Unit> unitsList;
	private ArrayList<Block> blocksList;
	
	/*
	 * Constructor
	 */
	public Map() {
		// Do nothing
	}
	
	public Map(String mName) {
		mapName = mName;
		attachMapTitle();
		calcMapHeightAndWidth();
		blocksList = new ArrayList<Block>();
		loadMap();
		unitsList = new ArrayList<Unit>();
		loadUnits();
	}
	
	/*
	 * Copy constructor
	 */
	public Map(Map map) {
		this.mapName = new String(map.getMapName());
		this.mapTitle = new String(map.getMapTitle());
		this.height = map.getWidth();
		this.width = map.getWidth();
		
		this.unitsList = new ArrayList<Unit>();
		for(int i = 0; i < map.getUnitsList().size(); i++)
			this.unitsList.add(new Unit(map.getUnitsList().get(i)));
		
		this.blocksList = new ArrayList<Block>();
		for(int i = 0; i < map.getBlocksList().size(); i++)
			this.blocksList.add(new Block(map.getBlocksList().get(i)));
	}
	
	/*
	 * Attach the map title with given map name
	 */
	private void attachMapTitle() {
		if(mapName.equals(MAP_NAME_0))
			mapTitle = MAP_TITLE_0;
		else if(mapName.equals(MAP_NAME_1))
			mapTitle = MAP_TITLE_1;
		else if(mapName.equals(MAP_NAME_2))
			mapTitle = MAP_TITLE_2;
		else if(mapName.equals(MAP_NAME_3))
			mapTitle = MAP_TITLE_3;
	}
	
	/*
	 * Calculate the width and height of the map (in number of entries)
	 */
	private void calcMapHeightAndWidth() {
		BufferedReader reader;
		try {
			String currentLine;
			reader = new BufferedReader(new FileReader("./src/Maps/" + mapName + ".txt"));
			
			/*
			 * Calculate width (say, number of columns)
			 */
			currentLine = reader.readLine();
			currentLine = currentLine.trim();
			currentLine = currentLine.replaceAll(" ", "");	// eliminate all spaces
			width = currentLine.length();
			
			/*
			 * Calculate height
			 */
			int rowCount = 1;	// Initialized to 1 because the first row has been counted
			while(true) {
				currentLine = reader.readLine();
				if(currentLine == null)
					break;
				currentLine = currentLine.trim();
				if(!currentLine.equals(""))
					rowCount++;
			}
			height = rowCount;
					
			reader.close();
		} catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	
	/*
	 * Load map into mapMatrix with given name
	 */
	private void loadMap() {
		BufferedReader reader;
		try {
			String currentLine;
			reader = new BufferedReader(new FileReader("./src/Maps/" + mapName + ".txt"));
			for(int row = 0; row < height; row++) {
				currentLine = reader.readLine();
				currentLine = currentLine.trim();
				currentLine = currentLine.replaceAll(" ", "");	// eliminate all spaces
				
				if(currentLine.isEmpty()) {	// the case that the current line is entirely blank
					row--;
					continue;
				}
				
				for(int column = 0; column < width; column++) {
					int blockTypeID = Character.getNumericValue(currentLine.charAt(column));
					
					/*
					 * Construct the list of blocks
					 */
					if(blockTypeID == Block.BLOCK_TYPE_ID_FIELD)
						blocksList.add(new BlockField(new Point(column, row)));
					else if(blockTypeID == Block.BLOCK_TYPE_ID_MIRE)
						blocksList.add(new BlockMire(new Point(column, row)));
					else if(blockTypeID == Block.BLOCK_TYPE_ID_WATER)
						blocksList.add(new BlockWater(new Point(column, row)));
					else if(blockTypeID == Block.BLOCK_TYPE_ID_MOUNTAIN)
						blocksList.add(new BlockMountain(new Point(column, row)));
					else if(blockTypeID == Block.BLOCK_TYPE_ID_FOREST)
						blocksList.add(new BlockForest(new Point(column, row)));
					else if(blockTypeID == Block.BLOCK_TYPE_ID_ROAD)
						blocksList.add(new BlockRoad(new Point(column, row)));
				}
			}
			
			reader.close();
		} catch(Exception ex) {
			// Exception handler
			ex.printStackTrace();
		}
	}
	
	/*
	 * Load units
	 */
	private void loadUnits() {
		BufferedReader reader;
		try {
			String currentLine;
			reader = new BufferedReader(new FileReader("./src/Maps/" + mapName + "_units.txt"));
			while(true) {
				currentLine = reader.readLine();				
				if(currentLine == null)	// Break if EOF
					break;
				
				/*
				 * Skip comments and blank lines
				 */
				currentLine = currentLine.trim();
				if(currentLine.startsWith("//"))
					continue;
				else if(currentLine.isEmpty())
					continue;
				
				/*
				 * Read in unit information
				 */
				int unitTypeID = Integer.valueOf(currentLine.substring(0, currentLine.indexOf("	")));
				currentLine = currentLine.substring(currentLine.indexOf("	") + 1);
				int unitPositionX = Integer.valueOf(currentLine.substring(0, currentLine.indexOf("	")));
				currentLine = currentLine.substring(currentLine.indexOf("	") + 1);
				int unitPositionY = Integer.valueOf(currentLine.substring(0, currentLine.indexOf("	")));
				currentLine = currentLine.substring(currentLine.indexOf("	") + 1);
				int side = Integer.valueOf(currentLine);
				
				/*
				 * Construct unit and add it to unitsList
				 */
				if(unitTypeID == Unit.UNIT_TYPE_ID_TANK)
					unitsList.add(new UnitTank(new Point(unitPositionX, unitPositionY), side));
				else if(unitTypeID == Unit.UNIT_TYPE_ID_MOBILEAA)
					unitsList.add(new UnitMobileAA(new Point(unitPositionX, unitPositionY), side));
				else if(unitTypeID == Unit.UNIT_TYPE_ID_INFANTRY)
					unitsList.add(new UnitInfantry(new Point(unitPositionX, unitPositionY), side));
				else if(unitTypeID == Unit.UNIT_TYPE_ID_BATTLESHIP)
					unitsList.add(new UnitBattleship(new Point(unitPositionX, unitPositionY), side));
				else if(unitTypeID == Unit.UNIT_TYPE_ID_JET)
					unitsList.add(new UnitJet(new Point(unitPositionX, unitPositionY), side));
			}
			reader.close();
		} catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	
	/*
	 * Mutators
	 */
	public void setMap(String mName) {
		mapName = mName;
		attachMapTitle();
		loadMap();
	}
	
	/* 
	 * Accessors
	 * Notice that getMapMatrix() only returns the map matrix that might be useful for
	 * path finding and visualization
	 */	
	public String getMapTitle() {
		return mapTitle;
	}
	
	public String getMapName() {
		return mapName;
	}
	
	public int getHeight() {
		return height;
	}
	
	public int getWidth() {
		return width;
	}
	
	public ArrayList<Unit> getUnitsList() {
		return unitsList;
	}
	
	public ArrayList<Block> getBlocksList() {
		return blocksList;
	}
}
